1.5-扩展数据类型
Create by fall on 13 Jul 2021 Recently revised in 27 Jun 2023
Array
Array 不是基本数据类型,是一个对象。
与其他编程语言中的数组一样,支持在单个变量名下存储多个元素,并具有执行常见数组操作的成员。
// 创建一个数组
var arr = [11,54,'啥都没输入']
实例方法
数组信息
Array.prototype.length
当前数组长度Array.prototype.at()
数组的某一个位置
const arr = [99,66,88,44]
arr.length // 4
arr.at(-1) // 44
const int8 = new Int8Array([0, 10, 42, -10]);
// first element
int8.at(0); // 0
// last element
int8.at(-1); // -10
数组操作
Array.prototype.push()
从末尾进行添加Array.prototype.pop()
从末尾取出Array.prototype.shift()
从最开始取出Array.prototype.unshift()
从最开始插入Array.prototype.concat()
合并两个数组
const arr = [1,2,3]
let length = arr.push(4) // 会返回 push 之后数组的长度,此时为 4
const num = arr.pop()
arr.unshift(0)
const start = arr.shift()
arr.concat(2,2,[5]) // 连接的时候,会自动去掉内容中最外层的一个 []
arr.concat([5,7,7])
数组的覆盖
Array.prototype.copyWithin(replaceStart,targetStart,targetEnd)
- i 必填:从数组的哪个下标开始替换,为负数,从后向前数
- 可选:targetStart,targetEnd 开始填充的位置(默认为 0),结束填充的位置(默认为原数组长度)
使用当前数组,复制当前数组的内容(从 [j,k)
),填充到当前数组
[1,2,3,4,5].copyWithin(2) // [1,2,1,2,3]
[1,2,3,4,5].copyWithin(-2) // [1,2,3,1,2]
[1,2,3,4,5].copyWithin(2,1) // [1,2,2,3,4]
[1,2,3,4,5].copyWithin(2,1,2) // [1,2,2,4,5]
Array.prototype.fill(content,start,end)
- content 表示将要覆盖的内容
- start, end 开始和结束的索引
[1,2,3,4,5].fill('z',1,3)
// [ 1, "z", "z", 4, 5 ]
把数组拍平
Array.prototype.flat()
把二维数组,或者是数组中嵌套数组的内容拍平
[1,2,[3,4,[5]]].flat() // [ 1, 2, 3, 4, [5] ]
// 通过 Infinity 可以直接全部展开为一维数组
[1,2,[3,4,[5]]].flat(Infinity) //[ 1, 2, 3, 4, 5 ]
截取数组
Array.prototype.splice(start,num,in)
该方法会从原数组上截取内容并且添加内容,并返回截取的内容(可以同时进行增、删、改)
- start 开始的项
- num 有几项。为零,表示不截取
- in 插入的内容
- result 返回截取的内容
let arr = [1,2,3]
// 删除
arr.splice(1,1)
// 插入
arr.splice(1,0,...['loa'])
// 替换
arr.splice(1,1,...['fake'])
Array.prototype.slice(start,end)
不会更改数组,只会返回截取的内容
参数都可以为负数,表示从后向前的位数,但截取 start end 规则不变
- start 截取的开始位置
- end 截取的结束位置
let arr = [5,6,4,6,2]
arr.slice() // [5,6,4,6,2]
arr.slice(2,3) // [4]
数组方法
Array.prototype.forEach
遍历数组,无返回值Array.prototype.map
每一个值都做指定操作Array.prototype.flatMap
遍历数组,返回数组会去掉一个括号,可以增加数组的长度
Array.prototype.filter
筛选出返回值为 true 的所有成员Array.prototype.reduce
把所有的值从前往后两两操作Array.prototype.reduceRight
功能和 reduce 相同,只不过方向相反
Array.prototype.every
每一项都为 true,则返回 trueArray.prototype.some
任意一项为 ture 返回 trueArray.prototype.find
遍历数组,查找到符合条件的内容Array.prototype.findIndex
遍历数组,返回符合的下标Array.prototype.findLast
遍历数组,返回最后一个符合的内容Array.prototype.findLastIndex
遍历数组,返回最后一个符合条件的内容的下标
let arr = [5,6,4,6,2]
// Array.prototype.map
let arr1 = arr.map(item=>item+2) //[ 7, 8, 6, 8, 4 ]
const staffs = [
{ name: "liu", age: 22 },
{ name: "zhang", age: 23 },
]
const staffInfo = staffs.map((staff) => {
return `员工:${staff.name},今年${staff.age}岁`;
});
// Array.prototype.filter
// 对数组的每一项进行筛选,返回符合条件的内容
let arr2 = arr.filter(item=>item>5) // [ 6, 6 ]
// Array.prototype.reduce 前一项和后一项进行操作,直至两两操作完成
let arr3 = arr.reduce((item1,item2)=>item1+item2) // 23
// every some 返回的是 true & false
let result = [1, 6, 9].every(item=>item<5) // 所有内容都需要符合条件,返回 true :且
let result = [1, 6, 9].some(item=>item>8) // 只要有一个内容符合条件即可:或
// Array.prototype.forEach
[1, 6, 9].forEacg(item=>console.log(item))
// Array.prototype.flatMap 的使用
let arr = [5,[4,6],2]
const arrFlatMap = arr.flatMap(item=>{
console.log(item)
return item // 除去一层数组符号
})
arrFlatMap // [ 5, 4, 6, 2 ]
const arrFlatMap1 = arr.flatMap(item=>{
console.log(item)
return [1,item] // 在每个 item 左侧都插入一个 1
})
arrFlatMap1 // [ 1, 5, 1, [4, 6], 1, 2 ]
// find, findIndex 用于查找内容和查找下标
[1, 6, 9].find((val, index, arr) => val % 2 === 0) // 返回 val
[1, 6, 9].findIndex((val, index, arr) => val % 2 === 0) // 返回 index
查找
查找并且返回 index
Array.prototype.indexOf(content,index)
从开始进行查找
Array.prototype.lastIndexOf()
从后向前查找
- content 查找的内容
- index 开始的查找点
[1,64,55,64,55,22,6].indexOf(55)
[1,64,55,64,55,22,6].lastIndexOf(55)
Array.prototype.includes(content)
当前数组是否包含所查找 content 内容
// 想要查看数组中是否包含某个元素 x,
// 原来的做法
if([].indexOf(x)>-1){}
if(~[].indexOf(x)){}
// 现在的做法
if([].includes(x))
可迭代对象
Array.prototype.keys()
Array.prototype.values()
Array.prototype.entries()
返回可迭代的键、值、键值对
var cc = ["1","2","6","66"]
for(i of cc.keys()){
console.log(i); // 1,2,3,4
}
for(i of cc.values()){
console.log(i); // "1","2","6","66"
}
for(i of cc.entries()){
console.log(i); // [ 0, "1" ] [ 1, "2" ] [ 2, "6" ] [ 3, "66" ]
}
并且可以检验数组中是否包含 NaN
,indexOf使用的是 ===
全等,无法在遇到 NaN
时返回 index。
[1, NaN, 3].indexOf(NaN) // -1
[1, NaN, 3].includes(NaN) // true
数组排序
Array.prototype.reverse()
数组倒序
Array.prototype.sort()
数组排序
const arr = [2,51,7,64,79,34]
arr.reverse() // [34, 79, 64, 7, 51, 2]
const arr1 = [2,51,7,64,79,34]
arr1.sort() // [2, 34, 51, 64, 7, 79]
const arr2 = [[2,22],[51,63],[7,82],[64,55],[79,99],[34,2]]
// sort 会根据返回值进行左右排列
// -1 左侧排到左侧 || 0 保持不变 || 1 左侧排到右边
arr2.sort((left,right)=>{
// return left[0]-right[0]
if(left[0]>right[0]){ // 左侧大于右侧
return 1 // left 值会放在右侧
}else{
return -1
}
}) // [ 2, 7, 34, 51, 64, 79 ]
数组转换
Array.prototype.toString()
转换为字符串返回
Array.prototype.toLocaleString()
转换为本地格式字符串返回
Array.prototype.join()
用指定的字符分割数组,并转换为字符串
ES 2023
这些内容需要 chrome 版本在 110 以上
原本的排序,移除,反序列,都是在原来的数组上进行操作,现在可以直接生成新的数组
Array.prototype.toSorted()
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const sorted = languages.toSorted();
console.log(sorted);
// [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]
console.log(languages);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]
Array.prototype.toSplice(start,num,in)
从原数组上 start
开始截取 num
长度的内容并且添加 in
内容,返回新的数组(可以同时进行增、删、改,且不改变原数组)
- start 开始的项
- num 有几项。为零,表示不截取
- in 插入的内容
- result 返回截取的内容
let arr = [1,2,3]
// 删除
arr.toSplice(1,1)
// 插入
arr.toSplice(1,0,...['loa'])
// 替换
arr.toSplice(1,1,...['fake'])
Array.prototype.toReverse()
const languages = [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
const reversed = languages.toReversed();
console.log(reversed); // ['CoffeeScript', 'TypeScript','JavaScript' ]
console.log(languages); // [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
Array.prototype.with()
更新数组指定 index 的内容并返回更新后的数组(不修改原数组)
const languages = [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
const after = languages.with(2,"rust");
console.log(after); // [ 'JavaScript', 'TypeScript', 'rust' ]
console.log(languages); // [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
静态方法
Array.isArray()
用来判断是否是数组
Array.isArray([]) // true
let ad = {
[0]:'vc',
length:1
}
Array.isArray(ad)
Array.from()
可以将字符串,类数组对象( NodeList
,arguments
),Set 和可迭代对象转化为数组
// 字符串转数组
Array.from("我搜搜") // [ "我", "搜", "搜" ]
// 数组合并,去重
let merge = [...[1,2,3],...[1,5,3]]
Array.from(new Set(merge))
function fun(){// 无论传入多少个数据,都可以拼接为数组,然后返回
return Array.from(arguments)
}
fun(1,5,6)
// 除此之外,第二个参数可以将每一个数据都进行一次操作
Array.from("我搜搜",x=>x+"_") // ["我_", "搜_", "搜_"]
Array.from({length: 3}, (·v, i) => ++i) // [1, 2, 3]
const map = new Map([["name","fall"],["age",23]])
Array.from(map) // [["name","fall"],["age",23]]
Array.of()
可以将参数转换为数组
Array.of('dfdfdfdf',5,64,79) // [ "dfdfdfdf", 5, 64, 79 ]
Set
Set 是一个键值统一的数据结构,可以近似理解为是数组,只不过数组的下标和数组的单项内容相同,可以通过传入一个数组,实现对于数组的构造。
const cc = new Set([1,2,2])
cc // Set([1,2])
const ss = new Set('unique')
ss // Set('u','n','i','q','e')
在 Set
内部判断两个值是否相等使用的是 sameValueZero
算法,需要注意条件和 ===
差不多,只有一个例外,NaN
和 NaN
会被认为相同。
let cc = new Set([NaN,NaN])
cc.size // 1
// 只要对象不指向同一个地址,就不会因为重复被筛选掉
var a = {title:'leaf'}
var b = {title:'leaf'}
var c = a
let set1 = new Set()
set1.add(a);set1.add(b);
set1.size // 2
let set2 = new Set()
set2.add(a);set2.add(c)
set2.size // 1
let set3 = new Set()
set3.add(a);set3.add({title:'leaf'})
set3.size // 2
Set 是有顺序的,根据添加的顺序进行排序,通过 for...of...
进行遍历
let set = new Set([1,3])
set.add('cs')
set.add('vc')
for(item of set){
console.log(item) // 1 3 'cs' "vc"
}
实例属性
let set = new Set([1,2])
set.add(5) // 向set内添加内容,返回 set
set.size // 返回set的个数
set.delete(2) // 删除元素,成功会返回true
set.has(2) // 是否拥有 2
set.keys() // 返回简明的遍历器
set.claer() // 清空当前Set
新增的原型方法
2024 年 6 月后,主流浏览器都可以使用该特性,而不需要 polyfill
Set.prototype.intersection()
返回两个 set 集合的交集Set.prototype.union()
返回两个 set 集合的并集Set.prototype.difference()
返回当前 set 除去给定 set 中的所有Set.prototype.symmetricDifference()
返回两个 set 不包括公共部分的其它部分Set.prototype.isSubsetOf()
判断当前 set 是否为输入 set 的子集Set.prototype.isSupersetOf()
判断当前 set 是否为输入 set 的父集Set.prototype.isDisjointFrom()
判断两个 set 是否有交集
应用场景
数组去重,字符串去重
Array.from(new Set([1,3,4,2,1,3])) //[1,3,4,2]
[...new Set([1,3,4,2,1,3])] //[1,3,4,2]
[...new Set('unique')].join('') // uniqe
求交集,差集,并集
let a = new Set('asd')
let b = new Set('asc')
let union = new Set([...a,...b]) // 并集
// 交集
let intersect = new Set([...a].filter(x=>b.has(x)))
// 差集,a-b的差集
let diff = new Set([...a].filter(x=>!b.has(x)))
WeakSet
和 Set 差不多,但是只能进行存储对象 WeakSet 的成员只能是对象,而不能是其他的值
-
WeakSet
只能是对象的集合,而不能是任何类型的任意值; -
WeakSet
持弱引用:集合中对象的引用为弱引用。如果没有其他的对WeakSet
中对象的引用,那么这些对象会被当成垃圾回收掉。这也意味着WeakSet
中没有存储当前对象的列表。正因为这样,WeakSet
是不可枚举的,也就没有size
属性,没有clear
和遍历的方法。 -
WeakSet.prototype.add(value)
:添加一个新元素value
; -
WeakSet.prototype.delete(value)
:从该WeakSet
对象中删除value
这个元素; -
WeakSet.prototype.has(value)
:返回一个布尔值, 表示给定的值value
是否存在于这个WeakSet
中;
Map
不同于对象,对象的键只能是字符串或者 Symbol
,而 Map
的键可以是任何类型(原始类型、对象或者函数),可以通过 Map
构造函数创建一个实例,入参是具有 Iterator
接口且每个成员都是一个双元素数组 [key, value]
的数据结构:
// Map的声明和添加
const map = new Map()
map.set({},12) // 设置一个键值对,可以通过此方法无线设置键值对
// 并且可以通过二元数组设置为Map
var arr = [['name',"fall"],['age',23]]
const map1 = new Map(arr)
运算特性
如果键值出现了重复,后面的数据会覆盖前面的数据
let map = new Map()
let foo = {foo: 'foo'}
map.set(foo, 'foo1') // 此处 foo作为键进行使用
map.set(foo, 'foo2')
map.get(foo) // 'foo2'
因为同 set 使用同样的 sameValueZero 算法,所以对于键名同为 NaN
以及相同对象而不同实例的处理同 Set
let a = NaN
let b = NaN
let map = new Map()
map.set(a, 'a')
map.set(b, 'b')
map.size // 1
map.get(a) // 'b'
原型方法
Map.prototype.size
:返回 Map
对象的键值对数量
Map.prototype.set(key, value)
:设置 Map
对象中键的值。返回该 Map
对象
Map.prototype.get(key)
: 返回键对应的值,如果不存在,则返回 undefined
Map.prototype.has(key)
:返回一个布尔值,表示 Map
实例是否包含键对应的值
Map.prototype.delete(key)
: 如果 Map
对象中存在该元素,则移除它并返回 true
Map.prototype.clear()
: 移除 Map
对象的所有键/值对
Map.prototype.keys()
:返回一个新的 Iterator
对象, 它按插入顺序包含了 Map
对象中每个元素的键
Map.prototype.values()
:返回一个新的 Iterator
对象,它按插入顺序包含了 Map
对象中每个元素的值
Map.prototype.entries()
:返回一个新的 Iterator
对象,它按插入顺序包含了 Map
对象中每个元素的 [key, value]
数组
Map.prototype.forEach(callbackFn[, thisArg])
:按插入顺序遍历 Map
let map = new Map()
map.set({a: 1}, 'a')
map.set({a: 2}, 'b')
for (let [key, value] of map) {
console.log(key, value)
}
// {a: 1} 'a'
// {a: 2} 'b'
for (let key of map.keys()) {
console.log(key)
// {a: 1}
// {a: 2}
}
WeakMap
- 只受对象(null 除外)作为键,键名所指的对象,不计入垃圾回收机制
- 类似于
Map
的结构,WeakMap
是不能被迭代的。
let wm = new WeakMap()
let foo = {name: 'foo'}
wm.set(foo, 'a') // Weak
wm.get(foo) // 'a'
wm.has(foo) // true
虽然 wm
的键对 foo
对象有引用,但是丝毫不会阻止 foo
对象被 GC
回收。当引用对象 foo
被垃圾回收之后,wm
的 foo
键值对也会自动移除,所以不用手动删除引用。
实例方法:
WeakMap.prototype.delete(key)
:移除key
的关联对象WeakMap.prototype.get(key)
:返回 key 关联对象, 或者 undefined(没有key关联对象时)WeakMap.prototype.has(key)
:根据是否有key
关联对象返回一个Boolean
值WeakMap.prototype.set(key, value)
:在WeakMap
中设置一组key
关联对象,返回这个WeakMap
对象
集合
有关集合的详细内容可查看存储扩展对象文件。
set
- 键值对相同
- 通过
add
方法进行添加 - 所有值不重复
let imgs = new Set()
imgs.add(100)
imgs.add('hello')
imgs.add('hello')
imgs.add(true)
imgs.add(new String('world'))
WeakSet
同 Set,但只能是对象的集合,意思就是WeakSet
中的东西是弱引用,如果没有其他的对WeakSet
中对象的引用,这些对象会被垃圾回收掉。
map
Map 集合的特点
- 通过键值区分不同的 Map
- 通过
add
方法进行添加 map
集合传值必须使用键值对进行添加
let map = new Map();
map.set('张三',"吃饭的");
map.set("六六","喝水的");
map.set("六六","吃药了");
console.log(map);
// Map对象取值
console.log(map.get('六六')); // 吃药了
WeakMap
同 Set,意思就是WeakMap
中的东西垃圾回收时不考虑,使用它不用担心内存泄漏问题
遍历
数组,和类数组对象(Map,Set)的遍历
数组的遍历
var arr = [10,22,33,44,55,66];
//1. for 循环
for(var i = 0;i<arr.length;i++){
document.write('for循环'+i+','+arr[i])
}
//2. for in 此时 i 指的是 index
for(var i in arr){
document.write('for in 循环'+ i+','+ arr[i])
}
//3. forEach
arr.forEach((item,index,arr)=>{
document.write('forEach'+index+','+item)
})
// 4. for...of
for(item of arr){
document.write("for...of"+item +"<br>")
}
map 的遍历
集合使用 for...in 不会出任何结果
var map = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
// 对map进行循环
for (let item of map) {
console.log(item[0],item[1])
}
// 将item解构
for(let [key,value] of map){
console.log(key,value)
}
set 的遍历
var mySet = new Set(['闪电','flash','too'])
console.log(mySet)
mySet.forEach(item => {
console.log(item)
})
mySet.forEach(function(item){
console.log(item)
})
// 以键值对的方式进行循环
for (let item of mySet.entries()) {
console.log(item)
}
// 以键的方式进行循环
for (let item of mySet.keys()) {
console.log(item)
}
扩展运算符
数据扩展运算符 ...
可以展开数组,以及 set
// 应用一:函数传参
Math.max(...[1, 2, 3]) // 3
// 应用二:数组合并
let merge = [...[1, 2], ...[3, 4], 5, 6] // 1, 2, 3, 4, 5, 6
// 应用三:浅克隆
let a = [1, 2, 3]
let clone = [...a]
a === clone // false
// 应用四:数组解构
const [x, ...y] = [1, 2, 3]
x // 1
y // [2, 3]
如果数组上没有值,ES6 新增语法就会将空位默认替换为 undefined
[...[1, , 3].values()] // [1, undefined, 3]
参考文章
作者 | 文章名称 |
---|---|
Phil Nash | ES2023 introduces new array copying methods to JavaScript |
Mozilla 官方文档 | https://developer.mozilla.org/en-US/blog/javascript-set-methods/ |